package org.budo.ip.location.service.lionsoul;

import java.io.File;

import org.budo.ip.location.service.api.BudoIpLocation;
import org.budo.ip.location.service.api.BudoIpLocationService;
import org.budo.support.assertion.Assert;
import org.budo.support.lang.util.FileUtil;
import org.budo.support.lang.util.IoUtil;
import org.budo.support.lang.util.StringUtil;
import org.budo.support.slf4j.Slf4j;
import org.budo.support.spring.io.util.ResourceUtil;
import org.lionsoul.ip2region.DataBlock;
import org.lionsoul.ip2region.DbConfig;
import org.lionsoul.ip2region.DbSearcher;
import org.slf4j.Logger;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import lombok.Getter;
import lombok.Setter;

/**
 * 通过查询 lionsoul 的数据包，得到一个ip所在的地址
 * 
 * @author lmw
 */
@Getter
@Setter
public class LionSoulIpLocationService implements BudoIpLocationService {
    private static final Logger log = Slf4j.getLogger();

    private static final String REGION_SPLIT_REGEX = "\\|";

    public static final String TMP_DIR = System.getProperty("java.io.tmpdir") + File.separator + "budo-ip-location-service-lionsoul-data-tmpdir";

    private DbSearcher dbSearcher;

    private Resource dbFileResource = new ClassPathResource("/META-INF/budo-ip-location-service-lionsoul-data/ip2region.db");

    @Override
    public BudoIpLocation seekIpLocation(String ip) {
        if (null == ip) {
            return null;
        }

        try {
            return this.doSeekIpLocation(ip);
        } catch (Throwable e) {
            throw new RuntimeException("#43 doSeekIpLocation error, e=" + e, e);
        }
    }

    private BudoIpLocation doSeekIpLocation(String ip) throws Throwable {
        DbSearcher dbSearcher = this.getDbSearcher();
        DataBlock dataBlock = dbSearcher.binarySearch(ip);

        if (null == dataBlock) {
            log.error("#39 dataBlock is null, ip=" + ip + ", dbSearcher=" + dbSearcher);
            return null;
        }

        return this.dataBlockToLionSoulIpLocation(dataBlock);
    }

    private BudoIpLocation dataBlockToLionSoulIpLocation(DataBlock dataBlock) {
        String region = dataBlock.getRegion();
        Assert.notEmpty(region, "dataBlock.region is null or empty, dataBlock=" + dataBlock);

        String[] regionArray = region.split(REGION_SPLIT_REGEX);
        Assert.arrayLength(regionArray, 5, "#61 region=" + region);

        LionSoulIpLocation ipLocation = new LionSoulIpLocation();

        String country = regionArray[0];
        ipLocation.setCountry(country);

        String province = regionArray[2];
        if (!StringUtil.isEmpty(province) && !"0".equals(province)) {
            ipLocation.setProvince(province);
        }

        String city = regionArray[3];
        if (!StringUtil.isEmpty(city) && !"0".equals(city)) {
            ipLocation.setCity(city);
        }

        return ipLocation;
    }

    public DbSearcher getDbSearcher() throws Throwable {
        if (null == this.dbSearcher) {
            synchronized (LionSoulIpLocationService.class) {
                if (null == this.dbSearcher) {
                    this.dbSearcher = this.initDbSearcher();
                }
            }
        }
        return this.dbSearcher;
    }

    private DbSearcher initDbSearcher() throws Throwable {
        Resource dbFileResource = this.getDbFileResource();
        Assert.notNull(dbFileResource, "dbFileResource can not be null");

        DbConfig dbConfig = null;
        String dbFileName = "ip2region.db";

        File dbFile = new File(TMP_DIR, dbFileName); // 临时文件
        IoUtil.write(ResourceUtil.getInputStream(dbFileResource), dbFile); // 复制文件内容

        DbSearcher dbSearcher = new DbSearcher(dbConfig, FileUtil.getCanonicalPath(dbFile));

        return dbSearcher;
    }
}